import telnetlib
import json
import datetime
from urllib.parse import unquote, urlparse
import random

import requests
from kazoo.client import KazooClient
from robot.api import logger
#
from Lib.zcm.assistant import FwVariables
from Lib.zcm.exception.TestException import TestException
from Lib.zcm.exception.errorcode import ErrorCode
from Lib.constants import *

def _to_str(arg):
    if isinstance(arg, tuple):  # ensure length == 2
        type, value = arg
    else:
        type, value, = None, arg
    if type == 'java.lang.String' or isinstance(value, str):
        return f'"{value}"'

    if isinstance(value, dict):
        if type:
            value.update({'class': type})
        return json.dumps(value)

    if isinstance(value, list):
        return json.dumps(value)  # todo

    return str(value)


class Response(object):
    def __init__(self, text, elapsed=None, is_success=True):
        self.text = text
        self.elapsed = elapsed
        self.is_success = is_success

    def json(self):
        return json.loads(self.text)


class Zookeeper(KazooClient):
    def get_services(self):
        group = FwVariables.get_value("DUBBO_CLIENT", "registry_group", "dubbo")
        path = f'/{group}'
        logger.debug(f'Try to get services with {path}')
        result = self.get_children(path)
        logger.debug(f'The services result {result}')

        exclude = ('com.alibaba.dubbo.monitor.MonitorService',)
        services = [item for item in result if item not in exclude]
        return services

    def get_providers(self, service):
        """从zk中获取Dubbo服务地址"""
        group = FwVariables.get_value("DUBBO_CLIENT", "registry_group", "dubbo")
        path = f'/{group}/{service}/providers'
        logger.debug(f"Try to get providers with {path} ...")
        result = self.get_children(path)
        logger.debug(f'The providers result {result}')
        providers = [urlparse(unquote(item)).netloc.split(':') for item in result]
        return providers


class Dubbo(telnetlib.Telnet):
    prompt = 'dubbo>'
    coding = 'utf-8'

    def __init__(self):
        self.zk_hosts = FwVariables.get_value("DUBBO_CLIENT", "zookeeper", None)
        self.timeout = int(FwVariables.get_value("DUBBO_CLIENT", "timeout", "300"))

        super().__init__(timeout=self.timeout)

    def _command(self, flag, str_="") -> bytes:
        result = self.read_until(flag.encode())
        self.write(str_.encode() + b"\n")
        return result

    def command(self, cmd: str) -> str:
        self._command(Dubbo.prompt, cmd)
        result = self._command(Dubbo.prompt, "")
        # result = result.decode(Dubbo.coding, errors='ignore').split('\n')[0].strip()
        result = result.decode(Dubbo.coding, errors='ignore')
        return result

    def ls(self, service=None):
        cmd = "ls" if service is None else f"ls {service}"
        data = self.command(cmd)
        data = data.split('\n')
        return data

    def get_services(self):
        return self.ls()

    def get_methods(self, service):
        return self.ls(service)

    def get_providers(self, service: str):
        if self.zk_hosts is None:
            raise ValueError('No zk_hosts set')
        logger.debug(f'connect the zookeeper witch {self.zk_hosts} ...')
        zk = Zookeeper(hosts=self.zk_hosts)
        logger.debug(f'connected!')

        zk.start()
        providers = zk.get_providers(service)
        zk.stop()
        return providers

    def invoke(self, service, method, args_str) -> str:
        if args_str:
            cmd = f"invoke {service}.{method}({args_str})"
        else:
            cmd = f"invoke {service}.{method}()"
        logger.debug(f'command-> {cmd}')
        result = self.command(cmd)
        logger.debug(f'result->  {result}')
        return result

    def request(self, service, method, args, host_name) -> Response:

        if host_name:
            self.host, self.port = host_name.split(':')
        if self.zk_hosts:
            providers = self.get_providers(service)
            if not providers:
                raise TestException.by_error_code(ErrorCode.UNKNOWN,
                                                  value='can not get prividers')
            self.host, self.port = random.choice(providers)
        else:
            raise TestException.by_error_code(ErrorCode.PARAM_ERROR,
                                              message='Dubbo host or zookeeper host are both none')
        logger.debug(f'Try to connect dubbo host {self.host}:{self.port} ...')
        self.open(self.host, self.port, self.timeout)
        self.write(b'\n')
        start_at = datetime.datetime.now()
        data = self.invoke(service, method, args)
        self.close()
        elapsed = datetime.datetime.now() - start_at
        result, is_ok = self._getResult(data)
        res = Response(result, elapsed=elapsed, is_success=is_ok)
        return res

    def _getResult(self, data):
        """解析反回响应
        Failed to invoke method getData, cause: java.lang.IllegalArgumentException: argument type mismatch
        -----------
        Use default service com.ztesoft.zsmart.bss.newbilling.api.profile.QueryProjectVersionService.
        result: "v91"
        elapsed: 5 ms.
        dubbo>
        """
        lines = data.split('\n')
        if len(lines) > 0 and lines[0].startswith('Failed'):
            # 报错了
            return lines[0], False
        elif len(lines) > 1:
            if lines[1].startswith('result:'):
                # 有返回值
                return lines[1][8:], True
            else:
                return lines[1], False
        else:
            return lines, False


class DubboCallByHttp:
    def __init__(self):
        # self.zk_hosts = FwVariables.get_value("DUBBO_CLIENT", "zookeeper", None)
        # self.registry_group = FwVariables.get_value("DUBBO_CLIENT", "registry_group", None)
        pass

    def request(self, link_name, interface, method, *args):
        zk_hosts = FwVariables.get_zk_address(link_name)
        registry_group = FwVariables.get_registry_group(link_name)
        time_out = FwVariables.get_post_dubbo_time_out(link_name)
        url = FwVariables.get_dubbo_test_url()
        if not url:
            raise TestException.by_error_code(ErrorCode.UNKNOWN,
                                              message='dubbo test url can not be none!')
        if not zk_hosts:
            raise TestException.by_error_code(ErrorCode.PARAM_ERROR,
                                              message='zookeeper host can not be none!')
        if not registry_group:
            raise TestException.by_error_code(ErrorCode.PARAM_ERROR,
                                              message='registry_group can not be none!')
        inputParamList = self.format_param(*args)

        data = {
            # "zkAddress": "172.16.25.132:2181",
            "zkAddress": zk_hosts,
            "zkName": zk_hosts,
            # "registryGroup": "CUST_REGISTRY_ZHY",
            "registryGroup": registry_group,
            "interfaceName": interface,
            "methodName": method,
            "inputParamList": inputParamList
        }
        headers = HEADERS
        headers['content-type'] = 'application/json'
        # rep = requests.post(url="http://dubbo.zsmart.com/dubbo/test", data=json.dumps(data), headers=headers)
        # rep = requests.post(url="http://localhost:9080/portal/zcm-cloudtest/dubbo/test", data=json.dumps(data), headers=headers, timeout=time_out)
        rep = requests.post(url=url, data=json.dumps(data), headers=headers, timeout=time_out)
        if rep.status_code != 200:
            raise TestException.by_error_code(ErrorCode.UNKNOWN, message=rep.text)
        result = json.loads(rep.text)
        if result["code"] != SUCCESS_CODE:
            raise Exception(result)
        return rep

    def format_param(self, *args):
        # 处理dubbo方法入参，可能会有多个
        new_parm_list = []
        if isinstance(args[0], list):
            parm_list = args[0]
        else:
            parm_list = args
        for param in parm_list:
            param["paramValueJson"] = json.loads(param["paramValue"])
            new_parm_list.append(param)
        return new_parm_list


Dubbo_Client = Dubbo()
Dubbo_Test_Client = DubboCallByHttp()
# rep = Dubbo_Client.request("com.ztesoft.bss.prod.cust.service.CustomerServiceExt", "updatePostpaidCreditInfo")
# print(rep.text)
